﻿//--------------------------------------------------------------------------
// <copyright file="CacheCollection.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
// <summary>CacheCollection</summary>
//--------------------------------------------------------------------------
namespace MS.Msn.MediaSolutions.Common
{
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Xml.Serialization;
    using System.Xml;
    using System.Collections.ObjectModel;
    using System.Collections;
    using System.Web;
    using System.Web.Caching;
    using System.IO;
    using System.Text.RegularExpressions;

    /// <summary>
    /// cache item collection
    /// </summary>
    [Serializable]
    public class CacheConfig : System.Configuration.IConfigurationSectionHandler
    {
        private static string configSectionName = "CacheConfig";

        /// <summary>
        /// cache collection config section name
        /// </summary>
        [XmlIgnore]
        public static string ConfigSectionName
        {
            get { return CacheConfig.configSectionName; }
            set { CacheConfig.configSectionName = value; }
        }

        private readonly Collection<CacheItem> cacheItems = new Collection<CacheItem>();

        /// <summary>
        /// cache item collection
        /// </summary>
        [XmlElement("CacheItem")]
        public Collection<CacheItem> CacheItems
        {
            get { return cacheItems; }
        }

        #region IConfigurationSectionHandler Members

        /// <summary>
        /// create custom config
        /// </summary>
        /// <param name="parent">parent node tag name</param>
        /// <param name="configContext">congig context</param>
        /// <param name="section">section tag name</param>
        /// <returns>config instance</returns>
        public object Create(object parent, object configContext, System.Xml.XmlNode section)
        {
            if (section != null)
            {
                configSectionName = section.Name;
                XmlSerializer ser = Xmls.GetXmlSerializer(this.GetType());
                return ser.Deserialize(new XmlNodeReader(section));
            }
            else
            {
                throw new ArgumentNullException(Apps.GetArgNullExString("section"));
            }
        }

        #endregion
    }

    /// <summary>
    /// cache item
    /// </summary>
    [Serializable]
    public class CacheItem
    {
        private string key = string.Empty;

        /// <summary>
        /// Cache Key
        /// </summary>
        [XmlAttribute]
        public string Key
        {
            get { return key; }
            set { key = value; }
        }

        // 12*60*60(12 Hours)
        private int expirationSeconds = 43200;  //12*60*60(12 Hours)

        /// <summary>
        /// expiration sectond count
        /// </summary>
        [XmlAttribute]
        public int ExpirationSeconds
        {
            get { return expirationSeconds; }
            set { expirationSeconds = value; }
        }
    }

    /// <summary>
    /// CacheMaster
    /// </summary>
    public static class CacheMaster
    {
        private static System.Web.Caching.Cache cacheInstance = System.Web.HttpRuntime.Cache;

        private readonly static TimeSpan DEFAULT_TIMESPAN = TimeSpan.FromSeconds(43200);  //12*60*60(12 Hours);

        #region access

        /// <summary>
        /// access cache
        /// </summary>
        /// <typeparam name="T">cache type</typeparam>
        /// <param name="cacheKey">key</param>
        /// <param name="setCacheProc">set cache proc</param>
        /// <param name="dpdObj">dependence object</param>
        /// <returns>cache item</returns>
        public static T AccessCacheItem<T>(string cacheKey, Proc<string, T> setCacheProc, T dpdObj)
        {
            T rtVal = default(T);

            object preCached = GetCacheItem(cacheKey);

            if (!object.Equals(preCached, null))
            {
                rtVal = (T)preCached;
            }

            if (object.Equals(dpdObj, default(T)) || object.Equals(dpdObj, null))
            {
                dpdObj = rtVal;
            }

            if (!object.Equals(rtVal, dpdObj) || object.Equals(rtVal, null))
            {
                rtVal = setCacheProc(cacheKey);
            }

            return rtVal;
        }

        /// <summary>
        /// access cache
        /// </summary>
        /// <typeparam name="T">cache type</typeparam>
        /// <param name="cacheKey">key</param>
        /// <param name="setCacheProc">set cache proc</param>
        /// <returns>cache item</returns>
        public static T AccessCacheItem<T>(string cacheKey, Proc<string, T> setCacheProc)
        {
            return AccessCacheItem<T>(cacheKey, setCacheProc, default(T));
        }

        #endregion

        #region check exist

        /// <summary>
        /// check exist
        /// </summary>
        /// <param name="cacheKey">cache key</param>
        /// <returns>is/not exist</returns>
        public static bool IsExist(string cacheKey)
        {
            return !object.Equals(cacheInstance.Get(cacheKey), null);
        }

        #endregion

        #region get cache

        /// <summary>
        /// get cache
        /// </summary>
        /// <param name="cacheKey">cache key</param>
        /// <returns>cache value</returns>
        public static object GetCacheItem(string cacheKey)
        {
            if (!IsExist(cacheKey))
            {
                return null;
            }
            else
            {
                return cacheInstance.Get(cacheKey);
            }
        }

        #endregion

        #region set cache

        /// <summary>
        /// set cache
        /// </summary>
        /// <param name="cacheKey">cache key</param>
        /// <param name="cacheObj">cache value</param>
        /// <param name="dpdFile">dependency</param>
        public static void SetCacheItem(string cacheKey, object cacheObj, string dpdFile)
        {
            CacheDependency dpd = null;

            if (!string.IsNullOrEmpty(dpdFile) && File.Exists(dpdFile))
            {
                dpd = new CacheDependency(dpdFile);
            }

            SetCacheItem(cacheKey, cacheObj, dpd);
        }

        /// <summary>
        /// set cache
        /// </summary>
        /// <param name="cacheKey">cache key</param>
        /// <param name="cacheObj">cache value</param>
        /// <param name="persist">persist</param>
        public static void SetCacheItem(string cacheKey, object cacheObj, TimeSpan persist)
        {
            if (IsExist(cacheKey))
            {
                cacheInstance[cacheKey] = cacheObj;
            }
            else
            {
                InsertCacheItem(cacheKey, cacheObj, null, persist);
            }
        }


        /// <summary>
        /// set cache
        /// </summary>
        /// <param name="cacheKey">cache key</param>
        /// <param name="cacheObj">cache value</param>
        /// <param name="expireTime">expiration time</param>
        public static void SetCacheItem(string cacheKey, object cacheObj, DateTime expireTime)
        {
            if (IsExist(cacheKey))
            {
                cacheInstance[cacheKey] = cacheObj;
            }
            else
            {
                TimeSpan ts = expireTime - DateTime.Now;
                InsertCacheItem(cacheKey, cacheObj, null, ts);
            }
        }

        /// <summary>
        /// set cache
        /// </summary>
        /// <param name="cacheKey">cache key</param>
        /// <param name="cacheObj">cache value</param>
        /// <param name="dpd">dependency</param>
        public static void SetCacheItem(string cacheKey, object cacheObj, CacheDependency dpd)
        {
            if (IsExist(cacheKey))
            {
                cacheInstance[cacheKey] = cacheObj;
            }
            else
            {
                InsertCacheItem(cacheKey, cacheObj, dpd, DEFAULT_TIMESPAN);
            }
        }

        /// <summary>
        /// set cache
        /// </summary>
        /// <param name="cacheKey">cache key</param>
        /// <param name="cacheObj">cache value</param>
        public static void SetCacheItem(string cacheKey, object cacheObj)
        {
            SetCacheItem(cacheKey, cacheObj, string.Empty);
        }

        #endregion

        #region remove cache

        /// <summary>
        /// remove all the cache items
        /// </summary>
        public static void Clear(Regex reg)
        {
            IDictionaryEnumerator de = cacheInstance.GetEnumerator();
            de.Reset();

            while (de.MoveNext())
            {
                if (!object.Equals(reg, null))
                {
                    string cKey = de.Key.ToString();

                    if (reg != null && reg.IsMatch(cKey))
                    {
                        cacheInstance.Remove(cKey);
                    }
                }
            }
        }

        /// <summary>
        /// clear caches for matched cache key
        /// </summary>
        /// <param name="match">string matched in cache key</param>
        public static void Clear(string match)
        {
            Regex reg = new Regex(match, RegexOptions.IgnoreCase);
            CacheMaster.Clear(reg);
        }

        /// <summary>
        /// clear all caches
        /// </summary>
        public static void Clear()
        {
            Regex reg = new Regex(@"\s*\S*", RegexOptions.IgnoreCase);
            CacheMaster.Clear(reg);
        }

        /// <summary>
        /// remove cache
        /// </summary>
        /// <param name="cacheKey">cache key</param>
        public static void RemoveCacheItem(string cacheKey)
        {
            if (!string.IsNullOrEmpty(cacheKey))
            {
                cacheInstance.Remove(cacheKey);
            }
            else
            {
                IDictionaryEnumerator cacheEnum = cacheInstance.GetEnumerator();

                while (cacheEnum.MoveNext())
                {
                    cacheInstance.Remove(cacheEnum.Key.ToString());
                }
            }
        }

        #endregion

        #region add cache

        /// <summary>
        /// add cache
        /// </summary>
        /// <param name="cacheKey">cache key</param>
        /// <param name="cacheObj">cache value</param>
        /// <param name="persist">persist</param>
        /// <param name="dpd">dependency</param>
        private static void InsertCacheItem(string cacheKey, object cacheObj, CacheDependency dpd, TimeSpan persist)
        {
            int expirationSeconds = -1;

            CacheConfig cc = null;
            cc = Apps.GetInstance<CacheConfig>(CacheConfig.ConfigSectionName, cc);

            if (!object.Equals(cc, null))
            {
                foreach (CacheItem cacheItem in cc.CacheItems)
                {
                    if (cacheKey.Contains(cacheItem.Key))
                    {
                        expirationSeconds = cacheItem.ExpirationSeconds;
                        break;
                    }
                }
            }

            if (expirationSeconds > 0)
            {
                persist = TimeSpan.FromSeconds(expirationSeconds);
            }

            cacheInstance.Add(cacheKey, cacheObj, dpd, DateTime.Now.Add(persist), System.Web.Caching.Cache.NoSlidingExpiration, CacheItemPriority.Default, null);
        }

        #endregion
    }
}
